package com.ccrfid.rmc6.service.mattress;

import com.ccrfid.rmc6.appconfig.websocket.MattressWebSocketHandler;
import com.ccrfid.rmc6.entity.mattress.BedAlarmEntity;
import com.ccrfid.rmc6.entity.mattress.BedAlarmView;
import com.ccrfid.rmc6.entity.mattress.BedEntity;
import com.ccrfid.rmc6.entity.mattress.MattressStateEntity;
import com.ccrfid.rmc6.pojo.Constants;
import com.ccrfid.rmc6.pojo.dto.mattress.MattressMsgDto;
import com.ccrfid.rmc6.repository.mattress.BedAlarmRepository;
import com.ccrfid.rmc6.repository.mattress.BedRepository;
import com.ccrfid.rmc6.repository.mattress.MattressStateRepository;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import vms.sleep.com.Bedhelp;

import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
@Transactional
public class MattressServiceImpl implements MattressService {
    private static final Logger logger = LoggerFactory.getLogger(MattressDataReceiver.class);

    @Autowired
    private EventBus eventBus;
    @Autowired
    private MattressStateRepository mattressStateRepository;
    @Autowired
    private BedRepository bedRepository;
    @Autowired
    private BedAlarmRepository bedAlarmRepository;
    @Autowired
    private MattressWebSocketHandler mattressWebSocketHandler;
    @Autowired
    private MattressAlarmService mattressAlarmService;

    private Map<String, MattressStateEntity> mattressStateCache = new ConcurrentHashMap<>();
    private Bedhelp bedhelp = new Bedhelp();

    //床垫离线判断时间间隔，毫秒
    @Value("${ht.mattress.time.offline}")
    private Integer timeOffline;
    //床垫数据记录间隔，用于减少数据库数据量
    @Value("${ht.mattress.time.interval}")
    private Integer timeInterval;

    @PostConstruct
    private void init() {
        eventBus.register(this);

        //设备离线检测线程
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        TimeUnit.MILLISECONDS.sleep((timeOffline != null && timeOffline > 0) ? timeOffline : 60000);
                        long timeNow = Instant.now().toEpochMilli();
                        for (String key : mattressStateCache.keySet()) {
                            MattressStateEntity cacheState = mattressStateCache.get(key);
                            if (timeNow - cacheState.getRecordTime().getTime() > timeOffline) {
                                //离线消息处理
                                if (bedRepository.existsByMattressSn(cacheState.getMattressSn())) {
                                    BedEntity bedEntity = bedRepository.getByMattressSn(cacheState.getMattressSn());
                                    BedAlarmView bedAlarm = new BedAlarmView();
                                    bedAlarm.setBedSn(bedEntity.getBedSn());
                                    bedAlarm.setMattressSn(bedEntity.getMattressSn());
                                    bedAlarm.setPersonnelId(bedEntity.getPersonnelId());
                                    bedAlarm.setPersonnelName(bedEntity.getPersonnelName());
                                    bedAlarm.setAlarmType(Constants.BED_ALARM_TYPE.OFFLINE.id);
                                    //推送离线消息
                                    mattressWebSocketHandler.inputBedAlarm(bedAlarm);
//                                    //保存离线告警消息
//                                    saveBedAlarmOffline(bedAlarm);
                                }
                                //删除离线设备
                                mattressStateCache.remove(key);
                            }
                        }
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }

    private void saveBedAlarmOffline(BedAlarmView bedAlarm) {
        BedAlarmEntity bedAlarmEntity = new BedAlarmEntity();
        bedAlarmEntity.setBedSn(bedAlarm.getBedSn());
        bedAlarmEntity.setMattressSn(bedAlarm.getMattressSn());
        bedAlarmEntity.setPersonnelId(bedAlarm.getPersonnelId());
        bedAlarmEntity.setPersonnelName(bedAlarm.getPersonnelName());
        bedAlarmEntity.setAlarmType(Constants.BED_ALARM_TYPE.OFFLINE.id);
        bedAlarmRepository.save(bedAlarmEntity);
    }

    @Subscribe
    public void onMattressMsgDto(MattressMsgDto msgDto) {
        MattressStateEntity stateEntity = new MattressStateEntity();
        stateEntity.setId(null);
        stateEntity.setMattressSn(msgDto.getSn());
        stateEntity.setProtocolVersion(msgDto.getV());
        stateEntity.setFirmwareVersion(msgDto.getD().getFv());
        stateEntity.setState(getState(msgDto.getD().getSt(), msgDto.getD().getWe()));
        stateEntity.setHeartRate(stateEntity.getState() == Constants.BED_STATE.OFF.id ? 0 : msgDto.getD().getHb());
        stateEntity.setBreathRate(stateEntity.getState() == Constants.BED_STATE.OFF.id ? 0 : msgDto.getD().getBr());
        stateEntity.setWeight(msgDto.getD().getWe());
        stateEntity.setWet(msgDto.getD().getWt() ? Constants.WET_STATE.YES.id : Constants.WET_STATE.NO.id);
        stateEntity.setPosition(msgDto.getD().getP());
        stateEntity.setSleepState(getSleepState(msgDto));
        stateEntity.setRecordTime(new Timestamp(Instant.now().toEpochMilli()));
        //
        inputMattressState(stateEntity);
    }

    @Override
    public Set<String> getAvailableMattresses() {
        return mattressStateCache.keySet();
    }

    private void inputMattressState(MattressStateEntity stateEntity) {
        if (mattressStateCache.containsKey(stateEntity.getMattressSn())) {
            MattressStateEntity cacheState = mattressStateCache.get(stateEntity.getMattressSn());
            if (stateEntity.getRecordTime().getTime() - cacheState.getRecordTime().getTime() > timeInterval) {
                //1.更新缓存
                mattressStateCache.put(stateEntity.getMattressSn(), stateEntity);
                //2.保存记录
                mattressStateRepository.save(stateEntity);
                //3.推送到告警服务
                mattressAlarmService.inputMattressState(stateEntity);
            }
        } else {
            //1.放入缓存
            mattressStateCache.put(stateEntity.getMattressSn(), stateEntity);
            //2.保存记录
            mattressStateRepository.save(stateEntity);
        }
        //3.推送数据到前端页面
        mattressWebSocketHandler.inputMattressState(stateEntity);
    }

    private Integer getState(String st, Integer we) {
        if (we <= 10) {
            return Constants.BED_STATE.OFF.id;
        } else if (we >= 17) {
            switch (st) {
                case "on":
                    return Constants.BED_STATE.ON.id;
                case "mov":
                    return Constants.BED_STATE.MOV.id;
                case "call":
                    return Constants.BED_STATE.CALL.id;
            }
        }
        return Constants.BED_STATE.OFF.id;
    }

    private Integer getSleepState(MattressMsgDto msgDto) {
        String sleepState = "";
        Integer weight = msgDto.getD().getWe();
        if (weight >= 17) {
            sleepState = bedhelp.GetSleepState(msgDto.getSn(), msgDto.getD().getBr(), msgDto.getD().getHb(), msgDto.getD().getSt());
        } else {
            //离床
            sleepState = bedhelp.GetSleepState(msgDto.getSn(), 0, 0, "off");
        }
        switch (sleepState) {
            case "0":
                return Constants.SLEEP_STATE.NOT.id;
            case "1":
                return Constants.SLEEP_STATE.START.id;
            case "2":
                return Constants.SLEEP_STATE.LIGHT.id;
            case "3":
                return Constants.SLEEP_STATE.DEEP.id;
            case "err":
                return Constants.SLEEP_STATE.ERR.id;
        }
        return Constants.SLEEP_STATE.NOT.id;
    }
}
